iT邦幫忙

2024 iThome 鐵人賽

DAY 29
0
生成式 AI

2024 年用 LangGraph 從零開始實現 Agentic AI System系列 第 29

【Day 29】- 網站開發遇上 AI:FastAPI、Streamlit 與 LangServe 的實戰指南

  • 分享至 

  • xImage
  •  

摘要
這篇文章旨在引導讀者學習如何利用 FastAPI 建構高效的後端服務,使用 Streamlit 打造互動式前端介面,並透過 LangServe 將 LangChain 模型輕鬆整合到應用程式中,最終打造一個智慧笑話生成器。文章結構清晰,首先概述了專案目標和使用的技術框架,接著詳細介紹了環境設定、後端開發、前端開發和 AI 整合等步驟,並包含了詳細的程式碼範例和圖示說明,以便讀者能夠輕鬆理解和實踐。整體而言,文章旨在為讀者提供一個全面的學習指南,幫助他們快速掌握使用這些技術框架開發 AI 驅動應用的核心技能,並激發他們在 AI 領域中開拓新機會。

引言

在當今快速發展的人工智慧時代,大型語言模型(LLM)應用已經成為各行各業的重要工具。從客戶服務到內容創作,從資料分析到決策支援,LLM 應用正在改變我們工作和生活的方式。然而,建構一個功能完整、效能優秀的 LLM 應用往往需要大量的時間和專業知識。

首圖

本教學旨在為您提供一個快速而實用的指南,幫助您在短時間內建構一個功能齊全的 LLM 應用。無論您是經驗豐富的開發者還是 AI 領域的新手,本教學都將為您提供清晰的步驟和深入的解釋,讓您能夠快速掌握建構 LLM 應用的核心技能。

2. 專案概述

我們的最終成果是一個智慧的笑話產生器應用。這個應用允許使用者輸入一個主題,然後利用 LLM 的強大能力產生與該主題相關的有趣笑話。這個看似簡單的應用實際上涵蓋了 LLM 應用開發的多個關鍵方面:

  1. 使用者介面: 直覺且具有互動性
  2. 後端服務: 高效且可擴展
  3. 整合 AI: 無縫且強大

streamlit 執行結果圖

為了實現這些功能,我們精心選擇了以下技術框架:

  • FastAPI: 作為我們的後端框架。FastAPI 以其高效能、易用性和自動 API 文件生成而聞名,非常適合建構高效的 AI 驅動的 Web 服務。
  • Streamlit: 用於建構我們的前端介面。Streamlit 允許我們使用純 Python 程式碼快速創建美觀的互動式 Web 應用,這對於專注於 AI 和資料科學的開發者來說特別有吸引力。
  • LangServe: 用於簡化 LangChain 應用的部署。LangServe 使我們能夠輕鬆地將 LangChain 的功能暴露為 API 端點,極大地簡化了 AI 功能的整合過程。

透過這個專案,您將學習如何:

  • 使用 FastAPI 建構高效的後端服務
  • 利用 Streamlit 創建直覺的使用者介面
  • 透過 LangServe 整合 AI 功能

3. 環境設定指南

在開始我們的 LLM 應用開發之旅之前,我們需要準備好開發環境。本指南將帶您完成從 GitHub 複製專案、設定 Conda 虛擬環境和安裝必要套件的過程。即使您是程式設計新手,只要按照以下步驟操作,也能輕鬆搭建好開發環境。

步驟 1: 安裝必要工具

  1. 安裝 Git:
    • 訪問 Git 官網 下載並安裝 Git。
    • 安裝後,打開終端機或命令提示字元,輸入 git --version 確認安裝成功。
  2. 安裝 Anaconda 或 Miniconda:
    • 訪問 Anaconda 官網 或 Miniconda 官網 下載適合您作業系統的安裝程式。
    • 執行安裝程式,按照提示完成安裝。
    • 安裝完成後,打開新的終端機,輸入 conda --version 確認安裝成功。

步驟 2: 複製專案

  1. 打開終端機。
  2. 導航到您想存放專案的目錄。
  3. 執行以下命令複製專案:
git clone https://github.com/Heng-xiu/agentic-system-lab-2024ironman

步驟 3: 創建 Conda 虛擬環境

  1. 打開終端機或 Anaconda Prompt,創建一個新的 Conda 環境:
conda create --name llm_app_env python=3.11
  1. 啟動環境
conda activate llm_app_env

步驟 4: 安裝套件

專案中應該已經包含了 requirements.txt 檔案。使用以下命令安裝套件:

pip install -r requirements.txt

步驟 5: 設定環境變數

  1. 在專案根目錄中找到 .env.example 檔案。
  2. 複製該檔案並重新命名為 .env。
  3. 打開 .env 檔案,填入您的 OpenAI API 金鑰:
OPENAI_API_KEY={"key": "sk-your_actual_api_key_here"}

請確保替換 your_actual_api_key_here 為您的實際 OpenAI API 金鑰。

4. 後端開發 (FastAPI)

在本章中,我們將探索如何使用FastAPI建構高效的後端服務,並透過LangServe來整合強大的AI功能。
我們的學習之旅將分為幾個關鍵階段:

  1. 首先,我們將深入了解FastAPI的基礎知識,包括它的優勢、核心概念以及如何建立一個基本的API服務。
  2. 接著,我們會逐步建構一個完整的FastAPI應用,從專案結構的設定到實現具體的API端點。
  3. 在掌握了FastAPI的基礎之後,我們將引入LangServe,這是一個專門用於部署LangChain應用的強大工具。我們將探討LangServe如何簡化AI驅動的API的開發過程。
  4. 最後,我們將學習如何將LangServe整合到我們的FastAPI專案中,創建一個智慧的後端服務,能夠處理複雜的AI任務。

透過這個過程,你將不僅學會如何使用FastAPI建立高效能的Web API,還將掌握如何將先進的AI功能無縫整合到你的後端服務中。讓我們開始這個激動人心的學習之旅吧!

4.1 FastAPI:現代化的高效能Web框架

FastAPI 是一個現代、快速(高效能)的 Web 框架,用於建構 API。它的主要用途包括:

  • 快速開發 RESTful API
  • 創建高效能的 Web 服務
  • 建構可擴展的微服務架構

FastAPI 的優勢在於:

  • 高效能:FastAPI 基於 Starlette 框架,提供了極高的效能,可以與 NodeJS 和 Go 相媲美。
  • 快速編碼:利用 Python 3.6+ 的型別提示,FastAPI 可以減少約 40% 的人為錯誤。
  • 標準化:基於開放標準 OpenAPI(前身是 Swagger)和 JSON Schema。
  • 自動文件:提供互動式 API 文件(Swagger UI)和可選的替代文件(ReDoc)。
  • 依賴注入系統:宣告式、強大且易於使用。
  • 安全性:包含安全的密碼雜湊、JWT 令牌、OAuth2 支援等。

4.1.1 API基礎:Web開發的核心概念

API(應用程式介面)是一組定義和協議,用於建構和整合應用程式軟體。在 Web 開發中,API 通常指的是一組 HTTP 端點,客戶端可以透過這些端點與伺服器進行通訊。

img

關鍵概念包括:

  • 端點(Endpoints):API 的特定 URL,代表特定的資源或功能。
  • HTTP 方法:如 GET(讀取)、POST(創建)、PUT(更新)、DELETE(刪除)等。
  • 請求和回應:客戶端發送的請求和伺服器返回的回應,通常使用 JSON 格式。
  • 狀態碼:表示請求結果的數字代碼,如 200(成功)、404(未找到)、500(伺服器錯誤)等。

例如,一個簡單的 API 端點可能看起來像這樣:

from fastapi import FastAPI

app = FastAPI()

@app.get("/hello")
async def read_root():
    return {"message": "Hello, World!"}

這個端點將回應 GET 請求到 /hello,返回一個 JSON 物件。

4.2 設定 FastAPI 專案

在開始建構 FastAPI 應用之前,了解一個好的專案結構為什麼重要是很有幫助的:

  • 組織性:好的結構使程式碼易於導航和維護。
  • 可擴展性:隨著專案成長,良好的結構使添加新功能變得容易。
  • 可讀性:清晰的結構幫助其他開發者(包括未來的你)快速理解專案。
  • 模組化:好的結構促進了程式碼的重用和模組化。

4.2.1 準備工作:安裝必要套件

pip install fastapi uvicorn

4.2.2 組織有序:專案的基本結構

讓我們從一個基本的結構開始,然後逐步擴展它:

my_fastapi_project/
│
├── app/
│   ├── __init__.py
│   ├── main.py
│   ├── api/
│   │   ├── __init__.py
│   │   └── health.py
│   └── core/
│       ├── __init__.py
│       └── config.py
│
├── requirements.txt
└── run_local.sh

解釋每個部分的作用:

  • app/: 這是我們應用的主要目錄。
    • main.py: 這是應用的進入點,我們在這裡創建 FastAPI 應用實例。
    • api/: 這個目錄包含所有的 API 路由。
      • health.py: 包含健康檢查端點。
    • core/: 包含核心功能和設定。
      • config.py: 儲存應用的設定設置。
  • requirements.txt: 列出專案的所有依賴。
  • run.sh: 一個方便的腳本來執行我們的應用。

4.2.3 應用核心:設定主應用檔案

現在,讓我們創建一個簡單的 main.py 檔案來開始我們的專案。

from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware

from app.core.config import settings
from app.api import health

app = FastAPI(
    title="鐵人賽專用",
    version="1.0",
    description="FastAPI LangGraph Streaming Service API 🚀",
)

# Set all CORS enabled origins
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

@app.get("/")
async def root():
    return {"message": "Welcome to My FastAPI App"}

if __name__ == "__main__":
    import uvicorn
    uvicorn.run(app, host="0.0.0.0", port=8000)

應用檔案的各個部分:

  • 我們導入必要的模組和我們的自訂模組。
  • 創建 FastAPI 應用實例,設定標題、版本和描述。
  • 添加 CORS 中介軟體以允許跨來源請求(這在實際部署時很重要)。
  • 定義一個簡單的根路由。
  • 提供一個直接執行應用的方法。

4.2.4 快速啟動:創建執行腳本

編輯 run_local.sh

#!/bin/bash
uvicorn app.main:app --reload --port 8000

使其可執行:

chmod +x run_local.sh

4.2.5 啟動應用:執行您的FastAPI應用

現在,您可以執行您的應用了:

./run_local.sh

您應該會看到類似以下的輸出:

INFO:     Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
INFO:     Started reloader process [28720]
INFO:     Started server process [28722]
INFO:     Waiting for application startup.
INFO:     Application startup complete.

4.2.6 初探成果:您的第一個API

打開瀏覽器,訪問 http://127.0.0.1:8000,您應該會看到:

{"message": "Welcome to My FastAPI App"}

4.2.7 自動文件:FastAPI的強大功能

FastAPI 自動為您生成了互動式 API 文件。訪問 http://127.0.0.1:8000/docs,您將看到 Swagger UI 介面,其中列出了所有可用的 API 端點。

img

恭喜你成功建立屬於自己的 API Server,並且基於 Fastapi 框架快速產生一版,讓我們繼續開發自己的 API 吧!

4.3 建構 FastAPI 應用:從 "健康檢查" 開始

4.3.1 實現健康檢查:建立/health API端點

首先,我們來創建一個簡單的健康檢查端點。這個端點通常用於監控服務的狀態,確保服務正在運行。

  1. app/api 目錄下創建 health.py 檔案:
from fastapi import APIRouter
from pydantic import BaseModel

router = APIRouter()

class HealthResponse(BaseModel):
    status: str

@router.get("/health", response_model=HealthResponse)
async def health_check():
    return {"status": "OK"}

這個簡單的端點將回應 GET 請求到 /health,返回一個 JSON 物件,表示服務正常運行。


輸入連結:http://127.0.0.1:8000/docs 就可看到

  1. main.py 中引入並註冊這個路由:
app.include_router(health.router, prefix="/api/v1", tags=["health"])

現在,當你訪問 http://localhost:8000/api/v1/health 時,你應該會看到 {"status": "ok"} 的回應。


輸入連結:http://localhost:8000/api/v1/health 就可看到

4.4 LangServe:輕鬆部署 LangChain 應用為 REST API

在我們深入探討 FastAPI 後端開發之前,讓我們先了解一下 LangServe。

LangServe 是一個專為部署 LangChain 應用而設計的工具,它能夠將 LangChain 的 runnables 和 chains 輕鬆轉換為 REST API。LangServe 與 FastAPI 緊密整合,提供了自動文件生成、高效的端點處理、互動式 playground 等功能,大大簡化了 AI 驅動應用的部署過程。

4.4.1 LangServe的優勢:功能豐富的API部署工具

  1. 自動推斷和強制執行模式:自動從您的 LangChain 物件推斷輸入和輸出模式,並在每次 API 呼叫時強制執行,提供豐富的錯誤訊息。
  2. API 文件:生成包含 JSONSchema 和 Swagger 的 API 文件頁面。
  3. 高效端點:提供 /invoke、/batch 和 /stream 端點,支援單個伺服器上的多個並行請求。
  4. 中間步驟串流:透過 /stream_log 端點支援串流傳輸鏈/代理的所有(或部分)中間步驟。
  5. 互動式 playground:在 /playground/ 提供帶有串流輸出和中間步驟的互動式頁面。
  6. LangSmith 整合:內建(可選)與 LangSmith 的追蹤整合,只需添加您的 API 金鑰即可使用。
  7. 客戶端 SDK:提供客戶端 SDK,允許像呼叫本地 Runnable 一樣呼叫 LangServe 伺服器。

4.4.2 LangServe實例:簡單而強大的AI應用

讓我們看一個極其簡單但功能強大的 LangServe 應用例子。這個應用使用 Anthropic 的 Claude 模型來生成笑話。儘管程式碼非常簡潔,但它創建了一個完整的 API,包括笑話生成、串流傳輸、模式文件,甚至還有一個互動式的 playground。

model = ChatAnthropic(model="claude-3-haiku-20240307")
prompt = ChatPromptTemplate.from_template("tell me a joke about {topic}")
add_routes(
    app,
    prompt | model,
    path="/joke",
)

使用這個簡單的設定,LangServe 自動為我們創建了以下端點:

POST /joke/invoke - 生成一個笑話
POST /joke/stream - 串流傳輸笑話生成過程
GET /joke/input_schema - 獲取輸入的 JSON schema
GET /joke/output_schema - 獲取輸出的 JSON schema

這個例子展示了 LangServe 如何讓複雜的 AI 應用部署變得異常簡單,使開發者能夠專注於創造有趣和有用的 AI 體驗,而不是陷入繁瑣的 API 開發細節中。

4.5 整合AI功能:將LangServe融入FastAPI專案

在我們的 FastAPI 專案中添加一個新的 AI 驅動的 /joke 端點是一個很好的方式來展示 LangServe 的功能。讓我們一步步來完成這個過程。

步驟一、建立笑話生成器:創建joke.py檔案

app/api/ 目錄下創建一個新的 joke.py 檔案:


router = APIRouter()

model = ChatOpenAI(api_key=settings.OPENAI_API_KEY['key'])
prompt = ChatPromptTemplate.from_template("tell me a joke about {topic}")
custom_chain = prompt | model

add_routes(
    router,
    custom_chain,
    path="/joke",
)

做了以下幾件事:

  • 創建了一個 FastAPI 的 APIRouter。
  • 設定了一個 OpenAI 的聊天模型和一個提示模板。
  • 使用 | 運算子將提示和模型組合成一個鏈。
  • 使用 LangServe 的 add_routes 函數將這個鏈添加到路由器中。

步驟二、 更新 main.py

確保在 main.py 檔案中包含新的 joke 路由:

from app.api import health, joke

# ... 其他程式碼 ...

app.include_router(health.router, prefix="/api/v1", tags=["health"])
app.include_router(joke.router, prefix="/api/v1", tags=["joke"])

# ... 其他程式碼 ...

步驟三、 執行伺服器

確保您的伺服器正在運行。如果還沒有啟動,請執行:

./run_local.sh

步驟四、測試 /joke API

現在,我們可以使用 curl 指令來測試我們的新 API 端點:

curl --location --request POST 'http://localhost:8000/api/v1/joke/invoke' \
    --header 'Content-Type: application/json' \
    --data-raw '{
        "input": {
            "topic": "cats"
        }
    }'

如果一切正常,您應該會收到一個關於貓的笑話作為回應。

你可以訪問 http://localhost:8000/docs 來查看完整的 API 文件,並進一步探索 LangServe 建立的端點。

透過完成這些步驟,我們已經成功地在我們的 FastAPI 應用中添加了一個由 LangServe 驅動的 AI 笑話生成器。這為我們後續與 Streamlit 的整合奠定了基礎。

5. Streamlit:打造互動式AI應用的前端利器

  • 介紹 Streamlit 的優點和基本概念。
  • 從簡單的文字輸入開始,逐步建構聊天介面。
  • 演示如何將前端與後端 API 連接。

5.1 Streamlit 簡介:輕量級前端開發的革新者

在這一節中,我們可以介紹 Streamlit 的基本概念和優點,Streamlit是一個開源Python函式庫,專為機器學習和資料科學專案的快速原型開發而設計。它的出現徹底改變了資料科學家和AI工程師建構互動式應用的方式。

5.1.1 Streamlit 的定位和特點

  • 簡潔的API:Streamlit提供了簡單直觀的API,使得即使是前端開發新手也能快速上手。
  • 即時更新:當你修改程式碼時,Streamlit會自動重新執行你的腳本,實現即時預覽。
  • 豐富的元件庫:從基本的文字輸入到複雜的資料視覺化,Streamlit都提供了現成的元件。
  • Python原生:使用純Python程式碼就能創建漂亮的Web應用,無需學習HTML、CSS或JavaScript。

5.1.2 為什麼選擇 Streamlit 進行AI應用開發

  1. 快速原型開發:Streamlit允許開發者在極短的時間內將AI模型轉化為可互動的Web應用。
  2. 專注於邏輯:開發者可以將更多精力放在應用邏輯和AI功能上,而不是前端細節。
  3. 易於整合:Streamlit可以輕鬆整合各種Python函式庫,包括用於AI和機器學習的函式庫。
  4. 部署簡單:Streamlit應用可以輕鬆部署到各種平台,包括Streamlit自己的免費託管服務。

5.1.3 Streamlit 與其他前端框架的比較

相比於傳統的Web開發框架 VAR(如Vue.js, Angular或React),Streamlit的學習曲線要平緩得多。它不要求開發者掌握前端技術棧,這對於資料科學家和AI工程師來說是一大優勢。然而,Streamlit的靈活性和自訂能力可能不如這些成熟的前端框架。

對比Flask或Django等Python Web框架,Streamlit更專注於資料展示和互動,而不是全端Web開發。它提供了更快的開發速度,但可能在處理複雜的後端邏輯時不如這些框架靈活。

5.2 前後端整合:連接Streamlit與FastAPI

在現代Web應用開發中,前後端分離已成為一種常見的架構模式。這種模式允許前端和後端獨立開發和部署,提高了開發效率和系統的可維護性。在我們的AI驅動的笑話生成器應用中,我們需要將Streamlit建構的使用者介面與FastAPI提供的後端服務連接起來,以實現完整的功能。

科技進步真快啊!從JSP模板到Streamlit和FastAPI,Web開發變得如此簡單高效,真讓人感慨萬分。

5.2.1 如何與API伺服器互動

在我們的應用中,Streamlit前端需要與FastAPI後端進行通訊以獲取笑話。這是透過HTTP請求實現的。讓我們逐步了解如何實現這一過程:

  1. 首先,我們導入必要的函式庫並設定API URL:
import streamlit as st
import requests

API_URL = "http://localhost:8000/api/v1/joke/invoke"

這裡我們導入Streamlit函式庫用於創建Web介面,requests函式庫用於發送HTTP請求。API_URL是我們FastAPI後端的地址,這裡假設它執行在本機的8000埠。

  1. 建立使用者介面
st.title("笑話產生器聊天")

# 初始化聊天歷史
if "messages" not in st.session_state:
    st.session_state.messages = []

# 顯示聊天歷史
for message in st.session_state.messages:
    with st.chat_message(message["role"]):
        st.markdown(message["content"])

# 使用者輸入
prompt = st.chat_input("您想要什麼樣的笑話?")

這部分程式碼創建了一個簡單的聊天介面:

  • st.title() 設定頁面標題。
  • 我們使用 st.session_state 來儲存聊天歷史,確保頁面重新整理時不會遺失對話。
  • for 迴圈遍歷並顯示所有歷史訊息。
  • st.chat_input() 創建一個聊天輸入框,讓使用者輸入笑話主題。
  1. 處理使用者輸入並發送 API 請求
if prompt:
    # 顯示使用者訊息
    st.chat_message("user").markdown(prompt)
    st.session_state.messages.append({"role": "user", "content": prompt})

    # 準備API請求
    req_json = {
        "input": {
            "topic": prompt
        }
    }
    
    # 發送請求到FastAPI伺服器
    response = requests.post(API_URL, json=req_json)
    
    # 處理回應
    if response.status_code == 200:
        assistant_response = response.json()["output"]["content"]
        # 顯示助理回應
        with st.chat_message("assistant"):
            st.markdown(assistant_response)
        st.session_state.messages.append({"role": "assistant", "content": assistant_response})
    else:
        st.error(f"錯誤:無法從伺服器獲取回應。狀態碼:{response.status_code}")

這是核心邏輯部分:

  • 首先檢查是否有使用者輸入(if prompt:)。
  • 如果有,我們將使用者的輸入顯示在聊天介面上,並添加到聊天歷史中。
  • 然後,我們準備API請求的JSON資料。
  • 使用 requests.post() 發送POST請求到FastAPI伺服器。
  • 如果請求成功(狀態碼200),我們解析回應,顯示笑話,並將其添加到聊天歷史中。
  • 如果請求失敗,我們顯示一個錯誤訊息。

5.2.2 如何啟動Streamlit應用

要啟動Streamlit應用,請按照以下步驟操作:

  1. 確保你已經安裝了Streamlit。如果沒有,可以使用pip安裝:
pip install streamlit
  1. 將上述程式碼儲存成 app.py,並執行以下指令:
streamlit run app.py
  1. Streamlit 將自動在你的預設瀏覽器中開啟應用。通常地址是 http://localhost:8501

streamlit 執行結果圖

透過這種方式,我們成功地將Streamlit前端與FastAPI後端整合,創建了一個互動式的AI驅動的笑話生成器。這個例子展示了Streamlit如何簡化前端開發過程,同時保持與後端服務的無縫整合。

在實際應用中,你可能還需要考慮添加更多的錯誤處理邏輯、實現請求逾時機制、添加使用者認證等功能。此外,對於大規模應用,你可能需要考慮如何優化效能,例如使用Streamlit的快取機制來減少不必要的API呼叫。

結論

在這個專案中,我們深入探索了如何結合現代 Web 開發技術和人工智慧,創建了一個有趣且實用的智慧笑話生成器應用。讓我們回顧一下我們的主要收穫:

  1. FastAPI 的強大功能:我們學習了如何使用 FastAPI 快速建構高效的後端 API。FastAPI 的自動文件生成、型別檢查和非同步支援等特性,大大提高了我們的開發效率和程式碼品質。

  2. Streamlit 的簡潔與靈活:透過 Streamlit,我們體驗了如何用純 Python 程式碼創建直觀、互動的 Web 介面。這個工具極大地降低了前端開發的門檻,使得資料科學家和 AI 工程師能夠輕鬆展示他們的工作成果。

  3. LangServe 的 AI 整合能力:我們探索了如何使用 LangServe 將複雜的 LangChain 應用轉化為簡單的 API 端點。這種方法大大簡化了 AI 功能的部署和整合過程。

  4. 前後端整合的實踐:透過將 Streamlit 前端與 FastAPI 後端連接,我們學習了現代 Web 應用的架構設計和實現方法。

  5. AI 應用開發的全流程:從環境設定到最終部署,我們體驗了開發 AI 驅動應用的完整流程,培養了全面的技術視角。

即刻前往教學程式碼 Repo,親自搭建屬於自己的 LLM App 吧!別忘了給專案按個星星並持續關注更新,讓我們一起探索AI代理的新境界。

#參考資料:

  1. Fastapi
  2. LangServe

上一篇
【Day 28】- 從零開始的 DSPy:打造高效翻譯錯誤檢測系統
下一篇
【Day 30】- 從 LangGraph 到使用者介面:整合 FastAPI 與 Streamlit 的全方位指南
系列文
2024 年用 LangGraph 從零開始實現 Agentic AI System31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言